package edu.poly.cs9223.assginment1;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
import net.schmizz.sshj.userauth.method.AuthPublickey;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.ConnectionInfo;
import ch.ethz.ssh2.KnownHosts;
import ch.ethz.ssh2.ServerHostKeyVerifier;

public class User {

	public String getLaunchConfigName() {
		return launchConfigName;
	}

	public void setLaunchConfigName(String launchConfigName) {
		this.launchConfigName = launchConfigName;
	}

	static Logger logger = LoggerFactory.getLogger(User.class);
	public SSHClient ssh;
	int maxInstanceAmount;
	String name;
	String securityGroupName;
	String privateKeyFingerPrint;
	String keyPath;
	String keyName;
	List<String> instanceIds = new ArrayList<String>();
	List<String> elasticIPs = new ArrayList<String>();
	String putMetricScaleUpAlarmName;
	String putMetricScaleDownAlarmName;
	List<String> aimIds = new ArrayList<String>();
	List<String> snapshotIds = new ArrayList<String>();
	String autoScalingGroupName = "";
	String snsTopicARN;
	String launchConfigName;
	TopicListener snsTopicListener;

	public String getElasticIpForSSH() {
		return elasticIPs.get(0);
	}

	public List<String> getElasticIP() {
		return elasticIPs;
	}

	public void addElasticIP(String elasticIP) {
		this.elasticIPs.add(elasticIP);
	}

	public List<String> getAmiIds() {
		return aimIds;
	}

	public void addAmiId(String amiId) {
		this.aimIds.add(amiId);
	}

	public int getMaxInstanceAmount() {
		return maxInstanceAmount;
	}

	public void setMaxInstanceAmount(int maxInstanceAmount) {
		this.maxInstanceAmount = maxInstanceAmount;
	}

	public String getPutMetricScaleUpAlarmName() {
		return putMetricScaleUpAlarmName;
	}

	public void setPutMetricScaleUpAlarmName(String putMetricScaleUpAlarmName) {
		this.putMetricScaleUpAlarmName = putMetricScaleUpAlarmName;
	}

	public String getPutMetricScaleDownAlarmName() {
		return putMetricScaleDownAlarmName;
	}

	public void setPutMetricScaleDownAlarmName(
			String putMetricScaleDownAlarmName) {
		this.putMetricScaleDownAlarmName = putMetricScaleDownAlarmName;
	}

	public TopicListener getSnsTopicListener() {
		return snsTopicListener;
	}

	public void setSnsTopicListener(TopicListener snsTopicListener) {
		this.snsTopicListener = snsTopicListener;
	}

	public String getSnsTopicARN() {
		return snsTopicARN;
	}

	public void setSnsTopicARN(String snsTopicARN) {
		this.snsTopicARN = snsTopicARN;
	}

	private static Integer SSH_CONNECTION_TIME_OUT = 10 * 60000;// (10min)

	public void addInstanceId(String instanceId) {
		this.instanceIds.add(instanceId);
	}

	public List<String> getInstanceIds() {
		return this.instanceIds;
	}

	public void addSnapshotId(String snapshotId) {
		this.snapshotIds.add(snapshotId);
	}

	public List<String> getSnapshotIds() {
		return this.snapshotIds;
	}

	public String getAutoScalingGroupName() {
		return autoScalingGroupName;
	}

	public void setAutoScalingGroupName(String autoScalingGroupName) {
		this.autoScalingGroupName = autoScalingGroupName;
	}

	public String getKeyName() {
		return keyName;
	}

	public void setKeyName(String keyName) {
		this.keyName = keyName;
	}

	public String getPrivateKeyFingerPrint() {
		return privateKeyFingerPrint;
	}

	public void setPrivateKeyFingerPrint(String privateKeyFingerPrint) {
		this.privateKeyFingerPrint = privateKeyFingerPrint;
	}

	public String getKeyPath() {
		return keyPath;
	}

	public void setKeyPath(String keyPath) {
		this.keyPath = keyPath;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
		putMetricScaleUpAlarmName = name + "-scale_up-alarm-metric";
		putMetricScaleDownAlarmName = name + "-scale_down-alarm-metric";
		launchConfigName = name + "_LaunchConfiguration";
		autoScalingGroupName = name + "_autoScalingGroup_"+UUID.randomUUID();
		securityGroupName = name + "_group_"+UUID.randomUUID();
		keyName = name + "_key";
	}

	public String getSecurityGroupName() {
		return securityGroupName;
	}

	public void setSecurityGroupName(String securityGroupName) {
		this.securityGroupName = securityGroupName;
	}

	public void sshLogin() throws Exception {

		logger.info(
				"[{}]【Connecting to instance】User [{}] is logging in via SSH,which may take couple of minutes to ssh into [{}],becuase we have to wait before instance changes it's state to running:) ",
				this.getName(), this.getName(), this.getElasticIpForSSH());
		ssh = new SSHClient();
		String hostPublicDNS = this.getElasticIpForSSH();
		logger.debug(
				"getting host finger print for instance:{},it may takes couple of minutes...",
				hostPublicDNS);
		String hostFingerPrint = null;
		while (hostFingerPrint == null)
			hostFingerPrint = this.getHostFingerPrint(hostPublicDNS);
		logger.debug("host:{} finger print is:{}", hostPublicDNS,
				hostFingerPrint);
		//
		ssh.addHostKeyVerifier(hostFingerPrint);
		ssh.connect(hostPublicDNS);

		// ssh.authPassword("kevin", "zzzzzz");
		PKCS8KeyFile keyFile = new PKCS8KeyFile();
		keyFile.init(new File(this.getKeyPath()));
		ssh.auth("root", new AuthPublickey(keyFile));

		logger.info(
				"[{}]【Connected】User [{}] has successfully connected to {} via SSH",
				this.getName(), this.getElasticIpForSSH());
	}

	private static String getHostFingerPrint(String hostName) {

		String sshServerAddr = hostName;
		Connection sshConnection = new Connection(sshServerAddr);
		try {
			sshConnection.connect(new ServerHostKeyVerifier() {
				public boolean verifyServerHostKey(String hostname, int port,
						String serverHostKeyAlgorithm, byte[] serverHostKey)
						throws Exception {
					return true;
				}
			}, SSH_CONNECTION_TIME_OUT, SSH_CONNECTION_TIME_OUT);
			ConnectionInfo info = sshConnection.getConnectionInfo();
			String keyAlgorithm = info.serverHostKeyAlgorithm;
			byte[] hostKeys = sshConnection.getConnectionInfo().serverHostKey;
			String fingerprint = KnownHosts.createHexFingerprint(keyAlgorithm,
					hostKeys);
			logger.debug("The host fingerprint is {} ", fingerprint);
			return fingerprint;
		} catch (IOException e) {
		} finally {
			sshConnection.close();
		}
		return null;
	}

	public void doSomething(int duration) throws InterruptedException {
		logger.info(
				"[{}]【50% CPU Load】User [{}] is generating 50% cpu load on {}....",
				this.getName(), this.getName(), this.getElasticIpForSSH());
		bash(duration / 2);
	}

	/**
	 * 
	 * @param duration
	 *            execution duration (unit:second)
	 * @throws InterruptedException
	 * @throws Exception
	 */
	public void doSomethingMore(int duration) throws InterruptedException {
		logger.info(
				"[{}]【100% CPU Load】User [{}] is generating 100% average cpu load on {}....",
				this.getName(), this.getName(), this.getElasticIpForSSH());
		bash(duration);
	}

	public void doNothing(int duration) throws InterruptedException {
		logger.info(
				"[{}]【0% CPU Load】User [{}] is generating 0% average cpu load on {}....",
				this.getName(), this.getName(), this.getElasticIpForSSH());
		Thread.sleep(duration*1000);
	}

	private void bash(int duration) {
		try {
			final Session session = ssh.startSession();
			// session.join(duration, TimeUnit.SECONDS);
			String bash = "(sleep " + duration
					+ " && killall dd) & dd if=/dev/zero of=/dev/null";// runs
																		// dd
																		// command
																		// with
																		// timelimit
			try {
				logger.debug("User:{} is executing {} on {}", this.getName(),
						bash, this.getElasticIpForSSH());
				final Command command = session.exec(bash);
				// String response =
				// IOUtils.readFully(command.getInputStream()).toString();
				command.join(duration, TimeUnit.SECONDS);
				return;
			} catch (IOException e) {
				if (logger.isDebugEnabled())
					e.printStackTrace();
			} finally {
				session.close();
			}
		} catch (ConnectionException e) {
			if (logger.isDebugEnabled())
				e.printStackTrace();
		} catch (TransportException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
